import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PerformanceBasedScheduler implements Scheduler {
	double V = 0;
	int bandwidth = 0;
	int time_slot = 1;
	double power = 0;
	int slot_number = 150000;

	/*
	 * for simulation
	 */
	private Group[] group_list;
	private int group_number = 5;
	private int level_number = 10;
	private int level_number_max = 1000;

	private BufferedReader signal_reader;
	private double energy_consumption = 0;
	private double signal_strength = 0;
	private Recorder recorder;

	public int data = 0;
	private int totally_delay = 0;
	private double metric_considering_app_char = 0;
	private int queue_length_all = 0;

	// 统计各个应用的能耗
	private double energy_consumption_each[];

	// 动态调整V参数
	double new_V;
	private int flag = 0;
	final private double multi_factor = 2;// 乘性增加的时候每一次乘的因子
	final private double add_factor = 0.001;// 加性增加的时候每一次加的因子
	final private double div_factor = 2;// 乘性减少的时候每一次除以的因子
	boolean startup_stage = true;// 标记启动阶段
	final private double expected_performance_metric = 0.8;
	private int t;

	int generated_data_number = 0;
	// 记录超时数据单元个数
	private double overdue = 0;

	// 2013.5.13修改。引入Tail作为决策的影响因素
	private int last_send = -100;// 记录下上一次发送数据的时刻，初始情况记为一个很小的值
	private double TailEnergy = 0;
	private double TailPower = 0;

	double overdue_number = 0;
	int queue_data = 0;
	double queue_performance_metric = 0;
	// 冲突产生的性能损失，数据量，能耗
	double overdue_perf_de = 0;
	double overdue_size = 0;
	double overdue_energy = 0;
	// 未冲突产生的性能损失，数据量，能耗
	double unod_perf_de = 0;
	double unod_size = 0;
	double unod_energy = 0;

	
	SignalGenerator sg=null;
	
	public PerformanceBasedScheduler(boolean Geometric) {
		sg=new SignalGenerator();
		// 初始化调度器
		group_list = new Group[group_number];

		recorder = new Recorder("-performance");

		// 第一个参数为deadline,第二个参数为上层应用每个时隙发送数据量的平均值（poisson分布）
		/**
		 * 权重为1:1:1的情况
		 */
		if(Geometric==true)
		{
		group_list[0] = new Group(10, 60, 0, 1.0/ 5, recorder);
		group_list[1] = new Group(200, 60, 1, 1.0/ 5, recorder);
		group_list[2] = new Group(400, 60, 2, 1.0/ 5, recorder);
		group_list[3] = new Group(800, 60, 3, 1.0/ 5, recorder);
		group_list[4] = new Group(1600, 60, 4, 1.0/ 5, recorder);
		}
//			/**
//			 * 权重为1/omega并且归一化的情况
//			 */
		else
		{
			group_list[0] = new Group(10, 60, 0, 160.0/ 175, recorder);
			group_list[1] = new Group(200, 60, 1, 8.0/175 , recorder);
			group_list[2] = new Group(400, 60, 2, 4.0/175, recorder);
			group_list[3] = new Group(800, 60, 3, 2.0/ 175, recorder);
			group_list[4] = new Group(1600, 60, 4, 1.0/ 175, recorder);
		}
		// group_list[0] = new Group(10, 60, 0,1/10,recorder);
		// group_list[1] = new Group(200, 60, 1,1/200,recorder);
		// group_list[2] = new Group(5000, 60, 2,1/5000,recorder);

		// group_list[0] = new Group(100, 60, 0,1,recorder);
		// group_list[1] = new Group(200, 60, 1,1,recorder);
		// group_list[2] = new Group(400, 60, 2,1,recorder);
		// group_list[3] = new Group(800, 60, 3,1,recorder);
		// group_list[4] = new Group(1600, 60, 4,1,recorder);

		// group_list[0] = new Group(100, 60, 0,1/100.0,recorder);
		// group_list[1] = new Group(200, 60, 1,1/200.0,recorder);
		// group_list[2] = new Group(400, 60, 2,1/400.0,recorder);
		// group_list[3] = new Group(800, 60, 3,1/800.0,recorder);
		// group_list[4] = new Group(1600, 60, 4,1/1600.0,recorder);

		energy_consumption_each = new double[group_number];

		try {
			signal_reader = new BufferedReader(new FileReader(new File("C:\\Users\\LZQ\\Desktop\\Lypunov_simulation_data\\signal_vector.csv")));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void begin_scheduling() {
		for (t = 0; t < slot_number; t += time_slot) {
			// 模拟整个过程
			// System.out.println("Time slot: " + t);
			// recorder.recordlog(new StringBuffer("#####slot" + t + "######\n"));
			// 读取信号强度，获得带宽，功率等参数
			signal_strength=sg.getSignal(t);
//			try {
//				signal_strength = Double.parseDouble(signal_reader.readLine());
//			} catch (NumberFormatException | IOException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
			V = getV();
			// if (V >2)
			// V = 2;// 设定V最大值
			// if (V < 0.01)
			// V = 0.01;// 设定V最小值
			// System.out.println("V= " + V);
			power = getPower();
			bandwidth = getBandwidth();
			double max_penalty = 0;
			TailPower = getTailPower(t);

			// 定义惩罚因子，为uij的系数aij
			double penalty_factor[][] = new double[group_number][level_number_max];
			for (Group gp : group_list) {
				gp.refreshState(t);// 更新Perf和Q_length
				for (int i = 0; i < level_number_max; i++)// 刷新每一组的惩罚因子
				{
					if (gp.queue_length[i] != 0)
						penalty_factor[gp.ID][i] = (gp.performance_penalty[i] / gp.queue_length[i])*gp.current_total_performance_penalty;
					else
						penalty_factor[gp.ID][i] = 0;
					if (penalty_factor[gp.ID][i] > max_penalty)
						max_penalty = penalty_factor[gp.ID][i];// 找到所有惩罚因子中最大的那一个
				}
			}
			generated_data_number = 0;
			for (int i = 0; i < group_number; i++)
				generated_data_number += group_list[i].app.generated_data_size;
			// for (int i = 0; i < group_number; i++) {
			// // 如果某一组中的最后一个子队列中有数据，即有的数据已经到达deadline了，那么把这个子队列的
			// // 惩罚因子调整成所有因子最大值+1，以保证之后尽可能的选择发送这些到达deadline了的数据
			// if (group_list[i].queue_length[level_number - 1] > 0)
			// penalty_factor[i][level_number - 1] = max_penalty + 1;
			// }

			// // 计算并记录惩罚因子之和
			// double penalty_factor_sum = 0;
			// for (int i = 0; i < group_number; i++)
			// for (int j = 0; j < level_number; j++) {
			// penalty_factor_sum += penalty_factor[i][j];
			// }
			// recorder.recordPenalty(penalty_factor_sum);
			// 尝试为每一个子队列分配带宽

			// 带宽分配向量，为i组j列分配uij带宽
			int u_vector[][] = new int[group_number][level_number_max];
			for (int[] i : u_vector)
				for (int j : i)
					j = 0;
			int allocated_bandwidth = 0;
			while (allocated_bandwidth < bandwidth) {
				// 每一次找出惩罚因子最大的那一个队列，为它分配带宽
				int max_gp_num = 0, max_level_num = 0;
				double max_factor = -1;
				for (int i = 0; i < group_number; i++)
					for (int j = 0; j < level_number_max; j++) {
						if (penalty_factor[i][j] > max_factor) {
							max_gp_num = i;
							max_level_num = j;
							max_factor = penalty_factor[i][j];
						}
					}
				// 找到了惩罚因子最大的那个子队列的编号，若此队列长度为0，说明已经没有数据可发了
				// 否则设置为一个极小值

				if (max_factor == -1) {
					break;
				}
				if (group_list[max_gp_num].queue_length[max_level_num] == 0) {
					penalty_factor[max_gp_num][max_level_num] = -10;
					continue;
				}

				penalty_factor[max_gp_num][max_level_num] = -10;
				// 接下来要针对这个惩罚因子最大的子队列分配带宽
				if (group_list[max_gp_num].queue_length[max_level_num] >= (bandwidth - allocated_bandwidth)) {
					// 如果不能发送完这个子队列中所有的数据，则部分发送
					// group_list[max_gp_num].sendData(max_level_num, bandwidth
					// - allocated_bandwidth);
					u_vector[max_gp_num][max_level_num] = bandwidth - allocated_bandwidth;
					allocated_bandwidth = bandwidth;
				} else {
					// 可以发送完这个子队列的数据
					allocated_bandwidth += group_list[max_gp_num].queue_length[max_level_num];
					// group_list[max_gp_num].sendData(max_level_num, bandwidth
					// - allocated_bandwidth);
					u_vector[max_gp_num][max_level_num] = group_list[max_gp_num].queue_length[max_level_num];
				}
			}

			// 此时为每一个子队列分配的带宽被保存于u_vector中
			// 计算决策变量D
			double D = 0;
			
			for (int i = 0; i < group_number; i++)
			{
				double perf_each=0;
				double f_u=0;
				for (int j = 0; j < level_number_max; j++) {
					if (group_list[i].queue_length[j] != 0)
//						D += u_vector[i][j] * group_list[i].performance_penalty[j] * group_list[i].performance_penalty[j]
//											/ group_list[i].queue_length[j];
					{
						/**
						 * 2013.6.17修改决策式
						 */
						perf_each+=group_list[i].performance_penalty[j];
						f_u+=(group_list[i].performance_penalty[j]/ group_list[i].queue_length[j])*u_vector[i][j];
					}
				}
				D+=perf_each*f_u;
				
			}
			
			// 决策：是否发送数据
			// recorder.recordD(D);
			recorder.recordlog(new StringBuffer("————D=" + D + "—————\n"));
			recorder.recordlog(new StringBuffer("————D - V * power=" + (D - V * power) + "—————\n"));
			recorder.recordlog(new StringBuffer("————current parameters: \n Q= " + queue_length_all + " \n u= " + bandwidth + " \n V= " + V
								+ " \n power= " + power + " \n"));

//			System.err.println((D - V * (power + TailPower)));
			if ((D - V * (power + TailPower)) >= 0) {
				// 发送数据
				for (int i = 0; i < group_number; i++)
					for (int j = 0; j < level_number_max; j++) {
						if (u_vector[i][j] != 0) {
							// 发送第i组，第j个子队列中的u_vector[i][j]大小数据
							recorder.recordlog(new StringBuffer("&&&&&&&&send data from group " + i + " level " + j + "  with " + u_vector[i][j]
												+ " data   "));
							group_list[i].sendData(j, u_vector[i][j], t, bandwidth, power);
							energy_consumption_each[i] += (power * u_vector[i][j]) / (1.0 * bandwidth);
							energy_consumption += (power * u_vector[i][j]) / (1.0 * bandwidth);
							data += u_vector[i][j];
						}
					}
				// 如果发送了数据，尾能耗增加
				if (allocated_bandwidth > 0) {
					TailEnergy += TailPower;
					last_send = t;
				}

				// if (allocated_bandwidth > 0)// 如果分配了带宽，那么说明有数据发送了，能耗增加
				// energy_consumption += power;
				// System.err.println("slot: "+t+" V: "+V+" Signal Strength: "+signal_strength+" Power: "+power+" BandWidth: "+bandwidth+" .");
				//
			} else {
				// 不发送数据
				// nothing to do

			}
			// 统计每个队列的总队长
			queue_length_all = 0;

			/**
			 * 这里，每个时隙内的总时延，和总的性能损失，可以看做2部分 一部分是由那些 所有被发送走的包所产生的时延，它们由发送时间-到达时间计算 另一部分是由那些缓存在调度器队列中的数据包产生的，它们有当前时间-到达时间计算
			 */
			totally_delay = 0;
			metric_considering_app_char = 0;
			overdue_number = 0;
			// 这里统计所有的由发送产生的时延
			for (int i = 0; i < group_number; i++) {
				for (int q : group_list[i].queue_length)
					queue_length_all += q;// 计算更新之后的队长，时延，metric值
				totally_delay += group_list[i].totally_delay;
				metric_considering_app_char += group_list[i].totally_metric_considering_app_char;
				overdue_number += group_list[i].overdue_number;
			}

			// 计算缓存在队列中的所有数据单元的时延、性能损失
			double queue_delay = 0;
			queue_performance_metric = 0;
			queue_data = 0;
			double queue_overdue = 0;
			for (int i = 0; i < group_number; i++) {
				for (DataUnit du : group_list[i].DataQueue) {
					queue_delay += 1.0 * (t - du.arr_time) * du.data.length;

					queue_performance_metric += group_list[i].omega * du.data.length * Penalty.f((t - du.arr_time), du.bound);

					// if ((t - du.arr_time) > du.bound) {
					// // queue_performance_metric += (t - du.arr_time) * group_list[i].max_penalty_factor * du.data.length;
					// queue_overdue+=du.data.length;
					// } else {
					// queue_performance_metric += (t - du.arr_time) * group_list[i].omega * du.data.length;
					// }

					queue_data += du.data.length;
					// if ((t - du.arr_time) > du.bound)
					// queue_overdue++;
				}
			}
			totally_delay += queue_delay;
			metric_considering_app_char += queue_performance_metric;

			// 记录超时数据单元字节个数
			overdue = 0;

			// overdue += group_list[i].overdue;// 发送数据包的overdue
			// overdue += queue_overdue;
			// 总的时延，性能损失还需要加上
			// recorder.recordQueueLength(queue_length_all);
			// recorder.recordEnergy(energy_consumption);
			// recorder.recordDelay(totally_delay);
			// recorder.recordMetric(metric_considering_app_char);
			// recorder.recordDelayPerByte(1.0 * totally_delay / (data + queue_data));
			// recorder.recordSendData(data);
			// recorder.recordOverdue(overdue);

			// 记录动态变化的V
			// recorder.recordV(V);

			// recorder.recordV_GP_BP(""+V+","+(metric_considering_app_char/t)+","+expected_performance_metric);
		}

		for (int i = 0; i < group_number; i++) {
			// 冲突产生的性能损失，数据量，能耗
			overdue_perf_de += group_list[i].overdue_perf_de;
			overdue_size += group_list[i].overdue_size;
			overdue_energy += group_list[i].overdue_energy;
			// 未冲突产生的性能损失，数据量，能耗
			unod_perf_de += group_list[i].unod_perf_de;
			unod_size += group_list[i].unod_size;
			unod_energy += group_list[i].unod_energy;
		}
		
		System.out.print((int) (energy_consumption + TailEnergy) + ",");// 1)总能耗
		System.out.print((metric_considering_app_char) + ",");// 2）总的性能损失量
		System.out.print((TailEnergy) + ",");// 3)尾能耗
		System.out.print((overdue_perf_de) + ",");// 4)冲突性能损失
		System.out.print((overdue_size) + ",");// 5)冲突数据量
		System.out.print((overdue_energy) + ",");// 6)冲突能耗
		System.out.print((unod_perf_de) + ",");// 7)未冲突性能损失
		System.out.print((unod_size) + ",");// 8)未冲突数据量
		System.out.print((unod_energy) + ",");// 9)未冲突能耗
		System.out.print((queue_performance_metric) + ",");// 10)队列中的性能损失
		System.out.print((generated_data_number) + ",");// 11)总产生的数据量
		System.out.print((data) + ",");// 12)发送的数据量
		System.out.print((queue_data) + "");// 13)缓存在队列中的数据量
		System.out.println();

		// System.out.println("Energy Consumption: " + (int) (energy_consumption+TailEnergy) + " J");
		// System.out.println("Tail Energy: " + (int) (TailEnergy) + " J");
		// // System.out.println("Totally dealy: " + totally_delay + " s*byte");
		// System.out.println("Totally metric considering app: " + metric_considering_app_char + " s*byte*(1/omega)");
		// System.out.println("Totally send data: " + (int) data + " byte");
		// // System.out.println("Current Queue Length: " + queue_length_all);
		// System.out.println("Generated Data Size: " + generated_data_number);
		// // System.out.println("Overdue number: " + overdue);
		// // System.out.println("Delay per byte: "+(1.0*totally_delay/generated_data_number));
		//
		// System.out.println("Energy Consumption for each Group:");
		// double e_a=0;
		// for(int i=0;i<group_number;i++)
		// e_a+=energy_consumption_each[i];
		// for(int i=0;i<group_number;i++)
		// System.out.println(energy_consumption_each[i]/e_a);
		//
		// System.out.println("\nPerformance Degradation for each Group:");
		// double p_a=0;
		// for(int i=0;i<group_number;i++)
		// p_a+=group_list[i].totally_metric_considering_app_char;
		// for(int i=0;i<group_number;i++)
		// System.out.println(group_list[i].totally_metric_considering_app_char/p_a);
		//
		// System.out.println("V factor: " + V + "\n");
		// recorder.recordV_GP_BP(""+V+","+(metric_considering_app_char/generated_data_number)+","+energy_consumption);
		// recorder.flush();
	}

	public void begin_scheduling(int number) {
		slot_number = number;
		begin_scheduling();
	}

	public double getV(int t) {
		double current_time_average_performance_metric = metric_considering_app_char / (t);
		if (t == 0)
			current_time_average_performance_metric = 0;
		// System.out.println("metric_considering_app_char= " + metric_considering_app_char + " t= " + t +
		// " current_time_average_performance_metric="
		// + current_time_average_performance_metric);

		// if (startup_stage) {
		// if (current_time_average_performance_metric < expected_performance_metric) {
		// // 在启动阶段，如果当前性能损失尚未到达预期，则让V乘性增加
		// // System.out.println("乘性增加");
		// return V * multi_factor;
		// } else {
		// // 启动阶段，当前性能损失已经超过了预期值，进入线性增加阶段
		// flag++;// 超过3次线性增加之后进入乘性减少阶段
		// if (flag == 1)
		// startup_stage = false;
		// return V /div_factor;
		// }
		// } else
		{
			if (current_time_average_performance_metric < expected_performance_metric) {
				// 在非启动阶段，如果当前性能损失尚未到达预期，则让V加性增加
				// System.out.println("加性增加");
				return V + add_factor;
			} else {
				return V / div_factor;
			}
		}
	}



	public double getTailPower(int t) {
		if (last_send == -100)
			return 0;
		if ((t - last_send) > 7) {
			// 计算完整的一个tail
			return 732 * 3 + 388 * 4;
		} else if ((t - last_send) > 3) {
			return (t - last_send - 3 - 1) * 388 + 732 * 3;

		} else {
			return (t - last_send - 1) * 732;
		}
	}

	public int getBandwidth() {
		return (int) (2.667*signal_strength +293.73);
	}
	public double getPower() {
		return -25 * signal_strength - 1030.9;

	}
	

	@Override
	public double getV() {
		// TODO Auto-generated method stub
		return V;
	}

	public void setV(double v) {
		V = v;
	}
}
